package net.jkdmanager.agent.services.engine;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import net.jkdmanager.agent.services.appinfo.ApplicationInfoService;
import net.jkdmanager.agent.services.configuration.AgentConfigurationService;
import net.jkdmanager.agent.services.plugins.AgentPluginService;
import net.jkdmanager.common.api.messages.ApplicationStatusesMessage;
import net.jkdmanager.common.api.model.Application;
import net.jkdmanager.common.api.plugins.AppInfoI;
import net.jkdmanager.common.api.plugins.DirectorI;
import net.jkdmanager.common.api.plugins.RunnerI;
import net.jkdmanager.common.api.plugins.RunnerI.Status;
import net.jkdmanager.common.services.messaging.MessagingService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
public class EngineServiceImpl implements EngineService {
	
	@Autowired
	private AgentConfigurationService agentConfigurationService;
		
	@Value("#{systemProperties.agentId}")
	private String agentId;
	
	@Autowired
	private MessagingService messagingService;
	
	@Autowired
	private AgentPluginService agentPluginService;
	
	@Autowired
	private ApplicationInfoService applicationInfoService;
	
	private Map<Long, RunnerI.Status> appStatuses = new ConcurrentHashMap<Long, RunnerI.Status>();
	
	@Scheduled(fixedDelay=5000)
	public synchronized void checkStatues() {
		
		for (Application app : agentConfigurationService.getAllApplications()) {
			
			AppInfoI appInfo = applicationInfoService.createApplicationInfo(app);
			
			DirectorI director = agentPluginService.getDirector(app.getId());
			RunnerI runner = agentPluginService.getRunner(app.getId());
			if (director.shallBeRunning(appInfo)) {
				//application is requested to be running
				if (runner.getStatus(appInfo) == Status.STOPED) {
					//it is not, let's start it
					startApplicationAsync(runner, appInfo);
				}
			} else {
				//application is supposed to stoped 
				if (runner.getStatus(appInfo) == Status.RUNNING) {
					//it is running, let's stop it async
					stopApplicationAsync(runner, appInfo);
				}
			}
		}
		
	}
	
	@Scheduled(fixedDelay=5000)
	protected synchronized void gatherStatuses() {
		
		Map<Long, RunnerI.Status> newAppStatuses = new ConcurrentHashMap<Long, RunnerI.Status>();
		
		for (Application app : agentConfigurationService.getAllApplications()) {
			
			AppInfoI appInfo = applicationInfoService.createApplicationInfo(app);
			
			if (app.getAgentId() == agentId) {
				RunnerI.Status status = agentPluginService.getRunner(app.getId()).getStatus(appInfo);
				newAppStatuses.put(app.getId(), status);
			}
			
		}
		
		appStatuses = newAppStatuses;
		sendApplicationStatusesUpdate();
		
	}
	
	private void sendApplicationStatusesUpdate() {
		
		ApplicationStatusesMessage message = new ApplicationStatusesMessage();
		message.setAppStatuses(appStatuses);
		message.setAgentId(agentId);
		messagingService.sendMessage(message);
		
	}
		
	@Async
	private void startApplicationAsync(RunnerI runner, AppInfoI appInfo) {
		runner.start(appInfo);
	}
	
	@Async
	private void stopApplicationAsync(RunnerI runner, AppInfoI appInfo) {
		runner.stop(appInfo);
	}
	
}
